package tanhua.dubbo.server.api;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.PageUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.dubbo.common.utils.CollectionUtils;
import com.alibaba.dubbo.common.utils.StringUtils;
import com.alibaba.dubbo.config.annotation.Service;
import com.alibaba.dubbo.config.spring.util.ObjectUtils;
import com.mongodb.client.result.DeleteResult;
import com.tanhua.dubbo.server.api.QuanZiApi;
import com.tanhua.dubbo.server.pojo.Album;
import com.tanhua.dubbo.server.pojo.Comment;
import com.tanhua.dubbo.server.pojo.Publish;
import com.tanhua.dubbo.server.pojo.TimeLine;
import com.tanhua.dubbo.server.vo.PageInfo;
import io.netty.util.internal.ObjectUtil;
import lombok.extern.slf4j.Slf4j;
import org.bson.types.ObjectId;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.redis.core.RedisTemplate;
import tanhua.dubbo.server.enums.CommentType;
import tanhua.dubbo.server.enums.IdType;
import tanhua.dubbo.server.service.IdService;
import tanhua.dubbo.server.service.TimeLineService;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CompletableFuture;

import static org.apache.commons.lang3.Range.is;

/**
 * @program: my-tanhua
 * @description:
 * @author: xuejingjing
 * @create: 2021-09-25 12:14
 **/

@Service(version = "1.0.0")
@Slf4j
public class QuanZiApiImpl implements QuanZiApi {


//    评论数据存储在Redis中的key的前缀
    private static final String COMMENT_REDIS_KEY_PREFIX = "QUANZI_COMMONT_";
//    用户是否点赞 的前缀
    private static final String COMMENT_USER_LIKE_REDIS_KEY_PREFIX = "QUANZI_COMMONT_";
//   用户是否喜欢  的前缀
    private static final String COMMENT_USER_LOVE_REDIS_KEY_PREFIX = "USER_LOVE_";




    @Autowired
    private MongoTemplate mongoTemplate;


    @Autowired
    private IdService idService;


    @Autowired
    private TimeLineService timeLineService;

    @Autowired
    private RedisTemplate<String,String> redisTemplate;


    @Override
    public PageInfo<Publish> queryPublishList(Long userId, Integer page, Integer pageSize) {
      //分析：查询好友的动态，实际上查询自己的时间线表，因为好友在发表动态的时候，会把他的发表动态的id写到自己的时间线里来。

        PageInfo<Publish> pageInfo = new PageInfo<>();
        pageInfo.setPageNum(page);
        pageInfo.setPageSize(pageSize);

        //分页
        Pageable pageable = PageRequest.of(page-1,pageSize, Sort.by(Sort.Order.desc("date")));
        //条件构造器
        Query query = new Query().with(pageable);
        List<TimeLine> timeLines = mongoTemplate.find(query, TimeLine.class, "quanzi_time_line_" + userId);
     //isNotBlack是String类型？
        if(CollectionUtils.isEmpty(timeLines)){
             return pageInfo; //没有查询到数据
        }
        //根据动态id查询动态列表

//    根据动态id查询动态列表    这是以前的方法，现在用hutool工具的方法
        List<ObjectId> ids = new ArrayList<>();
        for (TimeLine timeLine : timeLines) {
            //发布动态的朋友
            ObjectId publishId = timeLine.getPublishId();
            ids.add(publishId);
        }

        //Criteria 参数的构造
        Query query1 = Query.query(Criteria.where("id").in(ids))
                .with(Sort.by(Sort.Order.desc ("created")));
        List<Publish> publishes = mongoTemplate.find(query1, Publish.class);
        pageInfo.setRecords(publishes);
        return pageInfo;
    }


    /**
     * 发布动态
     * @param publish
     * @return 发布成功返回动态id
     */
    public String savePublish(Publish publish){

        //对publish对象校验
        if(!ObjectUtil.isAllNotEmpty(publish.getText(),publish.getUserId())){
            //发布失败
            return null;
        }

        /**
         * 动态发布成功后写入到发布表中
         */
        try {
            //设置主键id
            publish.setId(ObjectId.get());

            //设置自增长的pid
            publish.setPid(idService.created(IdType.PUBLISH));
            publish.setCreated(System.currentTimeMillis());
            //写入到publish表中
            mongoTemplate.save(publish);

            /**
             * 写入到相册表
             */
            Album album = new Album();
            album.setId(ObjectId.get());
            album.setCreated(System.currentTimeMillis());
            album.setPublishId(publish.getId());
            mongoTemplate.save(album,"quanzi_album_"+ publish.getUserId());
        } catch (Exception e) {
            //TODO 需要做事务的回滚，Mongodb的单节点服务，不支持事务，对于回滚待实现
            log.error("发布动态失败~publish="+ publish,e);
        }

        /**
         * 写入好友的时间线表（异步写入）
         */
        timeLineService.saveTimeLine(publish.getUserId(), publish.getId());
        return  publish.getId().toHexString();
    }

    @Override
    public PageInfo<Publish> queryRecommendPublishList(Long userId, Integer page, Integer pageSize) {
        PageInfo<Publish> pageInfo = new PageInfo<>();
        pageInfo.setPageNum(page);
        pageInfo.setPageSize(pageSize);
        //查询推荐的结果
        String key = "QUANZI_PUBLISH_RECOMMEND_" + userId;
        //通过这个key查询数据
        String data = redisTemplate.opsForValue().get(key);
        if(StringUtils.isEmpty(data)){
             return pageInfo;//想知道这个pageInfo为空返回的效果是怎么样的？
        }
        //用户hutool工具
        //查询到的pid进行分页处理
        List<String> pids = StrUtil.split(data, ',');
        //计算分页
        //[0, 10]
        int[] startEnd = PageUtil.transToStartEnd(page - 1, pageSize);
        int startIndex = startEnd[0]; //开始
        int endIndex = Math.min(startEnd[1], pids.size()); //结束

        List<Long> pidLongList = new ArrayList<>();
        for (int i = startIndex; i < endIndex; i++) {
            pidLongList.add(Long.valueOf(pids.get(i)));
        }

        if (CollUtil.isEmpty(pidLongList)) {
            //没有查询到数据
            return pageInfo;
        }

        return null;
    }

    /**
     * 根据发布动态id查询动态信息
     * @param id
     * @return
     */

    @Override
    public Publish queryPublishById(String id) {
        return  mongoTemplate.findById(id,Publish.class);

    }

    /**
     * 点赞 操作
     * 参数表示是某个人对某条动态进行点赞
     * @param userId
     * @param publishId
     * @return
     */
    @Override
    public Boolean likeComment(long userId, String publishId) {
        //判断该用户是否已经点赞，如果已经点赞，则直接返回
         if(queryUserIsLike(userId, publishId)){
             return false;
         }
         //保存comment数据
        Boolean result = saveComment(userId, CommentType.LIKE, null, publishId);
         //点赞失败
         if(!result){
             return false;
         }
         //点赞成功,也就是系统中已经保存了一条点赞记录
        //修改点赞数据
         String redisKey = COMMENT_REDIS_KEY_PREFIX + publishId;
         String hashKey = CommentType.LIKE.toString();
         redisTemplate.opsForHash().increment(redisKey,hashKey,1);

         //用户是否点赞
        String  userHashKey = COMMENT_USER_LIKE_REDIS_KEY_PREFIX +userId;

        redisTemplate.opsForHash().put(redisKey,userHashKey,"1");
         return true;
    }

//    private String getCommentRedisKeyPrefix(String publishId){
//
//    }

    /**
     * 取消点赞
     * @param userId
     * @param publishId
     * @return
     */
    @Override
    public Boolean disLikeComment(long userId, String publishId) {
        //判断当前用户是否对该条记录点赞
        Boolean aBoolean = queryUserIsLike(userId, publishId);
        if(!aBoolean){
            return false;
        }
        //取消点赞，就是把刚才点赞保存的数据删除掉
        Boolean aBoolean1 = removeComment(userId, publishId, CommentType.LIKE);
        //修改redis中的数据
        //修改点赞数
        String redisKey = COMMENT_REDIS_KEY_PREFIX + publishId;
        String hashKey = CommentType.LIKE.toString();
        //增加-1
        redisTemplate.opsForHash().increment(redisKey,hashKey,-1);

        //用户是否点赞
        String  userHashKey = COMMENT_USER_LIKE_REDIS_KEY_PREFIX +userId;
        redisTemplate.opsForHash().delete(redisKey,userHashKey);
        //相当于是取消这个点赞记录。
        return true;
    }


    private Boolean removeComment(Long userId,String publishId,CommentType commentType){

        //等价于 select* from Comment where userId = userId and publishId = publiId and commentType = 1;
        Query query = Query.query(Criteria.where("userId").is(userId).and("publishId").is(new ObjectId(publishId))
                .and("commentType").is(commentType.getType()));
        DeleteResult removeResult = mongoTemplate.remove(null, Comment.class);
        if(removeResult.getDeletedCount() >0){
            return true;
        }
        return false;
    }


    /**
     *  查询点赞数
     * @param publishId
     * @return
     */
    @Override
    public Long queryLikeCount(String publishId) {

        //先从redis中命中查询，如果命中直接返回即可。
        String redisKey = COMMENT_REDIS_KEY_PREFIX + publishId;
        String hashKey = CommentType.LIKE.toString();
        //相当于是查询 这个redis中，haskey 是喜欢类型的value,这个值都是存的数字
        Object data = redisTemplate.opsForHash().get(redisKey, hashKey);
        if(ObjectUtil.isNotEmpty()){
            return Convert.toLong(data);
        }
        //redis没有这个数据，就得从mogodb中查询
        Long counts = queryCommentCount(publishId, CommentType.LIKE);
        //写入到redis中
        // set = put
        redisTemplate.opsForHash().put(redisKey,hashKey,String.valueOf(counts));
        return counts;
    }


    /**
     * 查询数量
     * @param userId
     * @param publishId
     * @param commentType
     * @return
     */
    private Long queryCommentCount(String publishId,CommentType commentType){

        //等价于 select* from Comment where publishId = publiId and commentType = 1;
        Query query = Query.query(Criteria.("publishId").is(new ObjectId(publishId))
                .and("commentType").is(commentType.getType());
        long count = mongoTemplate.count(query, Comment.class);
        return count;
    }



    /**
     * 查询用户是否点赞该动态
     * @param userId
     * @param publishId
     * @return
     */
    @Override
    public Boolean queryUserIsLike(Long userId, String publishId) {

        //1.从redis中查询
        String redisKey = COMMENT_REDIS_KEY_PREFIX + publishId;
        String userHashKey = COMMENT_USER_LIKE_REDIS_KEY_PREFIX +userId;
        Object data = redisTemplate.opsForHash().get(redisKey, userHashKey);
        if(cn.hutool.core.util.ObjectUtil.isNotEmpty(data)){
            return StrUtil.equals(Convert.toStr(data),"1");
        }
        //查询mongodb,确定是否点赞
        Query query = Query.query(Criteria.where("publishId").is(new ObjectId(publishId))
                .and("userId").is(userId)
                .and("commentType").is(CommentType.LIKE)
        );
        long count = mongoTemplate.count(query, Comment.class);
        //如果等于0.说明用户肯定没有点赞
        if(count == 0){
            return  false;
        }

        //写入到redis中
        redisTemplate.opsForHash().put(redisKey,userHashKey,"1");
        return true;
    }

    @Override
    public Boolean lobeComment(Long userId, String publishId) {
        return null;
    }

    @Override
    public Boolean disLoveComment(Long userId, String publishId) {
        return null;
    }

    @Override
    public Long queryLoveCount(String publishId) {
        return null;
    }

    @Override
    public Boolean queryUserIsLove(Long userId, String publishId) {
        return null;
    }

    /**
     * 查询评论列表，按照发布时间正序
     * 也就是查询某个动态的所有评论
     * @param publishId
     * @param page
     * @param pageSize
     * @return
     */
    @Override
    public PageInfo<Comment> queryCommentList(String publishId, Integer page, Integer pageSize) {

        //分页+排序
        PageRequest pageRequest = PageRequest.of(page -1,pageSize,Sort.by(Sort.Order.asc("created")));

       Query query = Query.query(Criteria.where("publishId").is(new ObjectId(publishId))
       .and("commentType").is(CommentType.COMMENT.getType())).with(pageRequest);

        List<Comment> comments = mongoTemplate.find(query, Comment.class);
        //封装成需要的结果
        PageInfo<Comment> pageInfo = new PageInfo<>();
        pageInfo.setRecords(comments);
        pageInfo.setPageNum(page);
        pageInfo.setPageSize(pageSize);
        return pageInfo;
    }

    /**
     * 发表评论
     * @param userId
     * @param publishId
     * @param content
     * @return
     */
    @Override
    public boolean saveComment(Long userId, String publishId, String content) {
         return saveComment(userId, CommentType.COMMENT, content, publishId);

    }


    /**
     * 保存comment
     */
    private Boolean  saveComment(Long userId, CommentType commentType,String content,String publishId) {
        try {
            Comment comment = new Comment();
            comment.setId(ObjectId.get());
            comment.setCommentType(commentType.getType());
            comment.setContent(content);
            comment.setCreated(System.currentTimeMillis());
            comment.setUserId(userId);
            comment.setPublishId(new ObjectId(publishId));
            //根据publishId查询发布动态的人
            Publish publish = queryPublishById(publishId);
            if(cn.hutool.core.util.ObjectUtil.isNotEmpty(publish)){
                comment.setPublishUserId(publish.getUserId());
            }else{
                //此处省略

            }
            //TODO  其他评论对象，暂不处理
            comment.setPublishUserId(publish.getUserId());
            mongoTemplate.save(comment);
            return true;
        } catch (Exception e) {
            log.error("保存comment出错..." + e);
            e.printStackTrace();
        }
        return true;
    }

    @Override
    public PageInfo<Comment> queryLikeCommentListByUser(Long userId, Integer page, Integer pageSize) {
         return  queryCommentListByUser(userId,CommentType.LIKE,page,pageSize);
    }

    @Override
    public PageInfo<Comment> queryLoveCommentListByUser(Long userId, Integer page, Integer pageSize) {
        return  queryCommentListByUser(userId,CommentType.LOVE,page,pageSize);
    }

    @Override
    public PageInfo<Comment> queryCommentListByUser(Long userId, Integer page, Integer pageSize) {
        return  queryCommentListByUser(userId,CommentType.COMMENT,page,pageSize);
    }


    //这三个通用的方法
    private PageInfo<Comment> queryCommentListByUser(Long userId,CommentType commentType, Integer page, Integer pageSize){

        //创建这个的原因是Query查询的时候会用到
       PageRequest pageRequest =  PageRequest.of(page - 1,pageSize,Sort.by(Sort.Order.desc("create")));

       Query query = Query.query(Criteria.where("publishUserId").is(userId).and("commentType").is(commentType.getType())).with(pageRequest);

        List<Comment> commentList = mongoTemplate.find(query, Comment.class);
        PageInfo<Comment> pageInfo = new PageInfo<>();
        pageInfo.setPageNum(page);
        pageInfo.setPageSize(pageSize);
        pageInfo.setRecords(commentList);
         return pageInfo;
    }

    /**
     * 查询相册表
     */
    @Override
    public PageInfo<Publish> queryAlbumList(Long userId, Integer page, Integer pageSize) {

        PageInfo<Publish> publishPageInfo = new PageInfo<>();
        publishPageInfo.setPageSize(pageSize);
        publishPageInfo.setPageNum(page);

        PageRequest pageRequest = PageRequest.of(page -1,pageSize,Sort.by(Sort.Order.desc("created")));
        Query query = new Query().with(pageRequest);

        //查询自己的相册表
        List<Album> albumsList = mongoTemplate.find(query, Album.class, "quanzi" + userId);
        if (CollUtil.isEmpty(albumsList)) {
              return publishPageInfo;  //这样返回的话，岂不是前端也要跟着判断一次？？？
        }

        //根据相册表集合，筛选出所有的publish_id集合
        List<Object> publish_id = CollUtil.getFieldValues(albumsList, "publishId");
        Query queryPublish = Query.query(Criteria.where("id").in(publish_id)).with(Sort.Order.desc("created")));
        List<Publish> publishList = mongoTemplate.find(query, Publish.class);
        //获取他所有的id值，这样就可以不用sql语句
        publishPageInfo.setRecords(publishList);
        return publishPageInfo;
    }


}